{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Asynchronous Inference with OpenVINO™\n",
    "This notebook demonstrates how to use the [Async API](https://docs.openvino.ai/2024/openvino-workflow/running-inference/optimize-inference/general-optimizations.html) for asynchronous execution with OpenVINO.\n",
    "\n",
    "OpenVINO Runtime supports inference in either synchronous or asynchronous mode. The key advantage of the Async API is that when a device is busy with inference, the application can perform other tasks in parallel (for example, populating inputs or scheduling other requests) rather than wait for the current inference to complete first.\n",
    "\n",
    "\n",
    "#### Table of contents:\n",
    "\n",
    "- [Imports](#Imports)\n",
    "- [Prepare model and data processing](#Prepare-model-and-data-processing)\n",
    "    - [Download test model](#Download-test-model)\n",
    "    - [Load the model](#Load-the-model)\n",
    "    - [Create functions for data processing](#Create-functions-for-data-processing)\n",
    "    - [Get the test video](#Get-the-test-video)\n",
    "- [How to improve the throughput of video processing](#How-to-improve-the-throughput-of-video-processing)\n",
    "    - [Sync Mode (default)](#Sync-Mode-(default))\n",
    "    - [Test performance in Sync Mode](#Test-performance-in-Sync-Mode)\n",
    "    - [Async Mode](#Async-Mode)\n",
    "    - [Test the performance in Async Mode](#Test-the-performance-in-Async-Mode)\n",
    "    - [Compare the performance](#Compare-the-performance)\n",
    "- [`AsyncInferQueue`](#AsyncInferQueue)\n",
    "    - [Setting Callback](#Setting-Callback)\n",
    "    - [Test the performance with `AsyncInferQueue`](#Test-the-performance-with-AsyncInferQueue)\n",
    "\n",
    "\n",
    "### Installation Instructions\n",
    "\n",
    "This is a self-contained example that relies solely on its own code.\n",
    "\n",
    "We recommend  running the notebook in a virtual environment. You only need a Jupyter server to start.\n",
    "For details, please refer to [Installation Guide](https://github.com/openvinotoolkit/openvino_notebooks/blob/latest/README.md#-installation-guide).\n",
    "\n",
    "<img referrerpolicy=\"no-referrer-when-downgrade\" src=\"https://static.scarf.sh/a.png?x-pxid=5b5a4db0-7875-4bfb-bdbd-01698b5b1a77&file=notebooks/async-api/async-api.ipynb\" />\n"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Imports\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%pip install -q \"openvino>=2023.1.0\"\n",
    "%pip install -q opencv-python tqdm \"matplotlib>=3.4\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import time\n",
    "import numpy as np\n",
    "import openvino as ov\n",
    "from IPython import display\n",
    "import matplotlib.pyplot as plt\n",
    "from pathlib import Path\n",
    "\n",
    "# Fetch the notebook utils script from the openvino_notebooks repo\n",
    "import requests\n",
    "\n",
    "if not Path(\"notebook_utils.py\").exists():\n",
    "    r = requests.get(\n",
    "        url=\"https://raw.githubusercontent.com/openvinotoolkit/openvino_notebooks/latest/utils/notebook_utils.py\",\n",
    "    )\n",
    "    open(\"notebook_utils.py\", \"w\").write(r.text)\n",
    "\n",
    "import notebook_utils as utils\n",
    "\n",
    "# Read more about telemetry collection at https://github.com/openvinotoolkit/openvino_notebooks?tab=readme-ov-file#-telemetry\n",
    "from notebook_utils import collect_telemetry\n",
    "\n",
    "collect_telemetry(\"async-api.ipynb\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Prepare model and data processing\n",
    "[back to top ⬆️](#Table-of-contents:)\n",
    "\n",
    "### Download test model\n",
    "[back to top ⬆️](#Table-of-contents:)\n",
    "\n",
    "We use a pre-trained model from OpenVINO's [Open Model Zoo](https://docs.openvino.ai/2024/documentation/legacy-features/model-zoo.html) to start the test. In this case, the model will be executed to detect the person in each frame of the video."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "################|| Downloading person-detection-0202 ||################\n",
      "\n",
      "========== Retrieving model/intel/person-detection-0202/FP16/person-detection-0202.xml from the cache\n",
      "\n",
      "========== Retrieving model/intel/person-detection-0202/FP16/person-detection-0202.bin from the cache\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "\n",
    "# directory where model will be downloaded\n",
    "base_model_dir = \"model\"\n",
    "\n",
    "# model name as named in Open Model Zoo\n",
    "model_name = \"person-detection-0202\"\n",
    "precision = \"FP16\"\n",
    "model_path = Path(\"model\") / f\"{model_name}.xml\"\n",
    "\n",
    "base_model_url = \"https://storage.openvinotoolkit.org/repositories/open_model_zoo/2023.0/models_bin/1\"\n",
    "\n",
    "if not Path(model_path).exists():\n",
    "    utils.download_file(f\"{base_model_url}/{model_name}/{precision}/{model_name}.xml\", filename=model_path.name, directory=model_path.parent)\n",
    "    utils.download_file(\n",
    "        f\"{base_model_url}/{model_name}/{precision}/{model_name}.bin\", filename=model_path.name.replace(\".xml\", \".bin\"), directory=model_path.parent\n",
    "    )"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Select inference device\n",
    "[back to top ⬆️](#Table-of-contents:)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "c9c9f01fc0014058909e1d61e7bdd56d",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Dropdown(description='Device:', options=('CPU', 'AUTO'), value='CPU')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "device = utils.device_widget(default=\"CPU\")\n",
    "\n",
    "device"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Load the model\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# initialize OpenVINO runtime\n",
    "core = ov.Core()\n",
    "\n",
    "# read the network and corresponding weights from file\n",
    "model = core.read_model(model=model_path)\n",
    "\n",
    "# compile the model for the CPU (you can choose manually CPU, GPU etc.)\n",
    "# or let the engine choose the best available device (AUTO)\n",
    "compiled_model = core.compile_model(model=model, device_name=device.value)\n",
    "\n",
    "# get input node\n",
    "input_layer_ir = model.input(0)\n",
    "N, C, H, W = input_layer_ir.shape\n",
    "shape = (H, W)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create functions for data processing\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "def preprocess(image):\n",
    "    \"\"\"\n",
    "    Define the preprocess function for input data\n",
    "\n",
    "    :param: image: the orignal input frame\n",
    "    :returns:\n",
    "            resized_image: the image processed\n",
    "    \"\"\"\n",
    "    resized_image = cv2.resize(image, shape)\n",
    "    resized_image = cv2.cvtColor(np.array(resized_image), cv2.COLOR_BGR2RGB)\n",
    "    resized_image = resized_image.transpose((2, 0, 1))\n",
    "    resized_image = np.expand_dims(resized_image, axis=0).astype(np.float32)\n",
    "    return resized_image\n",
    "\n",
    "\n",
    "def postprocess(result, image, fps):\n",
    "    \"\"\"\n",
    "    Define the postprocess function for output data\n",
    "\n",
    "    :param: result: the inference results\n",
    "            image: the orignal input frame\n",
    "            fps: average throughput calculated for each frame\n",
    "    :returns:\n",
    "            image: the image with bounding box and fps message\n",
    "    \"\"\"\n",
    "    detections = result.reshape(-1, 7)\n",
    "    for i, detection in enumerate(detections):\n",
    "        _, image_id, confidence, xmin, ymin, xmax, ymax = detection\n",
    "        if confidence > 0.5:\n",
    "            xmin = int(max((xmin * image.shape[1]), 10))\n",
    "            ymin = int(max((ymin * image.shape[0]), 10))\n",
    "            xmax = int(min((xmax * image.shape[1]), image.shape[1] - 10))\n",
    "            ymax = int(min((ymax * image.shape[0]), image.shape[0] - 10))\n",
    "            cv2.rectangle(image, (xmin, ymin), (xmax, ymax), (0, 255, 0), 2)\n",
    "            cv2.putText(\n",
    "                image,\n",
    "                str(round(fps, 2)) + \" fps\",\n",
    "                (5, 20),\n",
    "                cv2.FONT_HERSHEY_SIMPLEX,\n",
    "                0.7,\n",
    "                (0, 255, 0),\n",
    "                3,\n",
    "            )\n",
    "    return image"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Get the test video\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "video_url = \"https://storage.openvinotoolkit.org/repositories/openvino_notebooks/data/data/video/CEO%20Pat%20Gelsinger%20on%20Leading%20Intel.mp4\"\n",
    "video_path = Path(\"data/test_video.mp4\")\n",
    "if not video_path.exists():\n",
    "    utils.download_file(video_url, video_path.name, video_path.parent)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## How to improve the throughput of video processing\n",
    "[back to top ⬆️](#Table-of-contents:)\n",
    "\n",
    "Below, we compare the performance of the synchronous and async-based approaches:\n",
    "\n",
    "### Sync Mode (default)\n",
    "[back to top ⬆️](#Table-of-contents:)\n",
    "\n",
    "Let us see how video processing works with the default approach.<br />\n",
    "Using the synchronous approach, the frame is captured with OpenCV and then immediately processed:\n",
    "\n",
    "![drawing](https://user-images.githubusercontent.com/91237924/168452573-d354ea5b-7966-44e5-813d-f9053be4338a.png)\n",
    "\n",
    "```\n",
    "while(true) {\n",
    "// capture frame\n",
    "// populate CURRENT InferRequest\n",
    "// Infer CURRENT InferRequest\n",
    "//this call is synchronous\n",
    "// display CURRENT result\n",
    "}\n",
    "```\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "def sync_api(source, flip, fps, use_popup, skip_first_frames):\n",
    "    \"\"\"\n",
    "    Define the main function for video processing in sync mode\n",
    "\n",
    "    :param: source: the video path or the ID of your webcam\n",
    "    :returns:\n",
    "            sync_fps: the inference throughput in sync mode\n",
    "    \"\"\"\n",
    "    frame_number = 0\n",
    "    infer_request = compiled_model.create_infer_request()\n",
    "    player = None\n",
    "    try:\n",
    "        # Create a video player\n",
    "        player = utils.VideoPlayer(source, flip=flip, fps=fps, skip_first_frames=skip_first_frames)\n",
    "        # Start capturing\n",
    "        start_time = time.time()\n",
    "        player.start()\n",
    "        if use_popup:\n",
    "            title = \"Press ESC to Exit\"\n",
    "            cv2.namedWindow(title, cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n",
    "        while True:\n",
    "            frame = player.next()\n",
    "            if frame is None:\n",
    "                print(\"Source ended\")\n",
    "                break\n",
    "            resized_frame = preprocess(frame)\n",
    "            infer_request.set_tensor(input_layer_ir, ov.Tensor(resized_frame))\n",
    "            # Start the inference request in synchronous mode\n",
    "            infer_request.infer()\n",
    "            res = infer_request.get_output_tensor(0).data\n",
    "            stop_time = time.time()\n",
    "            total_time = stop_time - start_time\n",
    "            frame_number = frame_number + 1\n",
    "            sync_fps = frame_number / total_time\n",
    "            frame = postprocess(res, frame, sync_fps)\n",
    "            # Display the results\n",
    "            if use_popup:\n",
    "                cv2.imshow(title, frame)\n",
    "                key = cv2.waitKey(1)\n",
    "                # escape = 27\n",
    "                if key == 27:\n",
    "                    break\n",
    "            else:\n",
    "                # Encode numpy array to jpg\n",
    "                _, encoded_img = cv2.imencode(\".jpg\", frame, params=[cv2.IMWRITE_JPEG_QUALITY, 90])\n",
    "                # Create IPython image\n",
    "                i = display.Image(data=encoded_img)\n",
    "                # Display the image in this notebook\n",
    "                display.clear_output(wait=True)\n",
    "                display.display(i)\n",
    "    # ctrl-c\n",
    "    except KeyboardInterrupt:\n",
    "        print(\"Interrupted\")\n",
    "    # Any different error\n",
    "    except RuntimeError as e:\n",
    "        print(e)\n",
    "    finally:\n",
    "        if use_popup:\n",
    "            cv2.destroyAllWindows()\n",
    "        if player is not None:\n",
    "            # stop capturing\n",
    "            player.stop()\n",
    "        return sync_fps"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test performance in Sync Mode\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAFoAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8rKKKKsAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA/9k=",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Source ended\n",
      "average throuput in sync mode: 55.59 fps\n"
     ]
    }
   ],
   "source": [
    "sync_fps = sync_api(source=video_path, flip=False, fps=30, use_popup=False, skip_first_frames=800)\n",
    "print(f\"average throuput in sync mode: {sync_fps:.2f} fps\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Async Mode\n",
    "[back to top ⬆️](#Table-of-contents:)\n",
    "\n",
    "Let us see how the OpenVINO Async API can improve the overall frame rate of an application. The key advantage of the Async approach is as follows: while a device is busy with the inference, the application can do other things in parallel (for example, populating inputs or scheduling other requests) rather than wait for the current inference to complete first.\n",
    "\n",
    "![drawing](https://user-images.githubusercontent.com/91237924/168452572-c2ff1c59-d470-4b85-b1f6-b6e1dac9540e.png)\n",
    "\n",
    "In the example below, inference is applied to the results of the video decoding. So it is possible to keep multiple infer requests, and while the current request is processed, the input frame for the next is being captured. This essentially hides the latency of capturing, so that the overall frame rate is rather determined only by the slowest part of the pipeline (decoding vs inference) and not by the sum of the stages.\n",
    "\n",
    "```\n",
    "while(true) {\n",
    "// capture frame\n",
    "// populate NEXT InferRequest\n",
    "// start NEXT InferRequest\n",
    "// this call is async and returns immediately\n",
    "// wait for the CURRENT InferRequest\n",
    "// display CURRENT result\n",
    "// swap CURRENT and NEXT InferRequests\n",
    "}\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "def async_api(source, flip, fps, use_popup, skip_first_frames):\n",
    "    \"\"\"\n",
    "    Define the main function for video processing in async mode\n",
    "\n",
    "    :param: source: the video path or the ID of your webcam\n",
    "    :returns:\n",
    "            async_fps: the inference throughput in async mode\n",
    "    \"\"\"\n",
    "    frame_number = 0\n",
    "    # Create 2 infer requests\n",
    "    curr_request = compiled_model.create_infer_request()\n",
    "    next_request = compiled_model.create_infer_request()\n",
    "    player = None\n",
    "    async_fps = 0\n",
    "    try:\n",
    "        # Create a video player\n",
    "        player = utils.VideoPlayer(source, flip=flip, fps=fps, skip_first_frames=skip_first_frames)\n",
    "        # Start capturing\n",
    "        start_time = time.time()\n",
    "        player.start()\n",
    "        if use_popup:\n",
    "            title = \"Press ESC to Exit\"\n",
    "            cv2.namedWindow(title, cv2.WINDOW_GUI_NORMAL | cv2.WINDOW_AUTOSIZE)\n",
    "        # Capture CURRENT frame\n",
    "        frame = player.next()\n",
    "        resized_frame = preprocess(frame)\n",
    "        curr_request.set_tensor(input_layer_ir, ov.Tensor(resized_frame))\n",
    "        # Start the CURRENT inference request\n",
    "        curr_request.start_async()\n",
    "        while True:\n",
    "            # Capture NEXT frame\n",
    "            next_frame = player.next()\n",
    "            if next_frame is None:\n",
    "                print(\"Source ended\")\n",
    "                break\n",
    "            resized_frame = preprocess(next_frame)\n",
    "            next_request.set_tensor(input_layer_ir, ov.Tensor(resized_frame))\n",
    "            # Start the NEXT inference request\n",
    "            next_request.start_async()\n",
    "            # Waiting for CURRENT inference result\n",
    "            curr_request.wait()\n",
    "            res = curr_request.get_output_tensor(0).data\n",
    "            stop_time = time.time()\n",
    "            total_time = stop_time - start_time\n",
    "            frame_number = frame_number + 1\n",
    "            async_fps = frame_number / total_time\n",
    "            frame = postprocess(res, frame, async_fps)\n",
    "            # Display the results\n",
    "            if use_popup:\n",
    "                cv2.imshow(title, frame)\n",
    "                key = cv2.waitKey(1)\n",
    "                # escape = 27\n",
    "                if key == 27:\n",
    "                    break\n",
    "            else:\n",
    "                # Encode numpy array to jpg\n",
    "                _, encoded_img = cv2.imencode(\".jpg\", frame, params=[cv2.IMWRITE_JPEG_QUALITY, 90])\n",
    "                # Create IPython image\n",
    "                i = display.Image(data=encoded_img)\n",
    "                # Display the image in this notebook\n",
    "                display.clear_output(wait=True)\n",
    "                display.display(i)\n",
    "            # Swap CURRENT and NEXT frames\n",
    "            frame = next_frame\n",
    "            # Swap CURRENT and NEXT infer requests\n",
    "            curr_request, next_request = next_request, curr_request\n",
    "    # ctrl-c\n",
    "    except KeyboardInterrupt:\n",
    "        print(\"Interrupted\")\n",
    "    # Any different error\n",
    "    except RuntimeError as e:\n",
    "        print(e)\n",
    "    finally:\n",
    "        if use_popup:\n",
    "            cv2.destroyAllWindows()\n",
    "        if player is not None:\n",
    "            # stop capturing\n",
    "            player.stop()\n",
    "        return async_fps"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test the performance in Async Mode\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAFoAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8rKKKKsAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA/9k=",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Source ended\n",
      "average throuput in async mode: 75.17 fps\n"
     ]
    }
   ],
   "source": [
    "async_fps = async_api(source=video_path, flip=False, fps=30, use_popup=False, skip_first_frames=800)\n",
    "print(f\"average throuput in async mode: {async_fps:.2f} fps\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Compare the performance\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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brcrWLCgSpUqJUnaunWr1e78/6pVq7rdLjQ0VKVLl07XHhcXpzNnzki6dLupO++80+3DeZ/mffv2uZ0/I61bt1bRokWVkpKiL774wmq/cOGCZsyYIcn13tFXKlmypMaPH6/9+/dr586dmjVrlp577jnr/bFixQo1bdpU//zzj626nO+9evXqWRdFc3rkkUfk7++vw4cP64cffnDpK1GihLp06aIzZ86oXr16atCggQYPHqy5c+dmWsMTTzwhSZoyZYpL+0cffSRJeuihh5Q/f36320ZGRloXtruSu/f8n3/+KUkqVKiQKlWqlGFNnihcuLBCQ0PTtTvfz5K99/y2bdskSYGBgRn+zN96663WFdud7/OEhAQdPnxYUsbv+cqVK8vPzy9d+6ZNmyRJvr6+uueeezJ8z2/evFmS/fc8ANyMCNIA4AFfX1/VqFFDPXr00Keffqpdu3apffv2kqTvv/9eK1eudBl/3333qUSJEtq6dat+++03q90Zbh5//PEMnysoKCjDPh+fS/+Mm/9/tWdJSkxMtGoMDg62uWdXl1E9zuBztf7La3WGIR8fH5dgciXnldIvD0/O/3cGK3fc9V0e/mJjYzN87N+/X5Ks0J1V/v7+evTRRyX9322uJGn+/Pk6fvy48ubNq86dO2dprvLly+vBBx/U66+/rm3btunll1+WdCno2LkF1vnz5/X5559LunS17CsVLlxY9957ryT395SeMmWKXn31VVWsWFGrVq3SuHHj1K5dOxUtWlQdOnRQfHx8um26deumgIAA/fzzz9q7d6+kS6+l848L/8Z7PiwsLMPtPHW193NWxrh7z2f2vvXz81PhwoVdxl/+3s9oW19fXxUqVChdu/M9n5KSkul7/tixY5Lsv+cB4GZEkAaAbBAaGqqpU6dav+RfGaT9/PysWx1NnjxZ0qVb2MTFxalQoUJWCM8OISEhkqTU1FSXlTBv5FyRTEtL09GjRzMcd+jQIZfxl///kSNHMtzOXZ/zjwsOh0MXL16UuXSaU4aPzG5llRHn93rdunX666+/JP1fqG7fvr1Hgc/X11fPP/+8atWqJSn9eywzc+bM0YkTJyRJzzzzjHUrs8sfc+fOlXTpD0FXfi/8/f01YMAAbd++Xfv27dOXX36p3r17K3/+/Prmm2901113pXuvFSpUSA888IDS0tKscD5r1iwlJiaq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AAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQAAAACADQRpAAAAAABsIEgDAAAAAGADQRoAAAAAABsI0gAAAAAA2ODVQfrbb7/V3XffrUKFCilv3rwqV66cOnXqpH379rmMS0xMVExMjMLDwxUQEKCIiAgNGDBASUlJOVQ5AAAAACC38svpAtwxxqhPnz768MMPVaFCBT388MPKnz+/Dh48qGXLlmnPnj0qU6aMJCk5OVnR0dHauHGjWrRooU6dOmnDhg0aP368li1bpuXLlytv3rw5vEcAAAAAgNzCK4P0m2++qQ8//FBPPfWU3nzzTfn6+rr0X7x40fr/V199VRs3btSgQYM0duxYq33w4MEaN26cJk2apCFDhly32gEAAAAAuZvDGGNyuojLnT17VqVKlVKBAgW0bds2+fllnPWNMSpdurQSExN1+PBhBQUFWX3JyckqXry4ihYtqp07d2b5+RMTExUaGqqEhASFhIRc074AAHAjihj8fU6XAADIheLHts7pEjJlJwt63Yr0Tz/9pH/++Uc9evRQamqq5s2bp+3btyssLEx33XWXKlasaI2Ni4vTwYMH1bJlS5cQLUlBQUFq2LChFi5cqH379lmHggMAAAAAcC28LkivW7dOkuTr66uoqCht377d6vPx8VG/fv00fvx4SZeCtCRFRka6nSsyMlILFy5UXFxchkE6JSVFKSkp1teJiYnZsh8AAAAAgNzJ667affToUUnSxIkTFRoaqtWrV+v06dNavny5KlWqpAkTJui9996TJCUkJEiSQkND3c7lXI53jnNnzJgxCg0NtR6sXAMAAAAAMuN1QTotLU2SlCdPHs2ZM0d169ZVcHCwGjVqpK+++ko+Pj6aMGFCtj3fkCFDlJCQYD2uvLUWAAAAAACX87pDu52ry3Xq1FHJkiVd+qpVq6by5ctrx44dOnXqlDU2oxVn52HaGa1YS1JAQIACAgKyo3QAAAAAwE3A61akK1euLEkKCwtz2+9sP3v2rHVutPNc6Std7RxqAAAAAADs8roV6aZNm0qStmzZkq7vwoUL2rFjh4KCglSkSBEVL15cJUuWVGxsrJKTk9Pd/io2NlblypXjvGcAAAAAQLbxuhXpChUqqEWLFtqxY4cmT57s0jd27FidOnVK7du3l5+fnxwOh3r16qWkpCSNHj3aZezo0aOVlJSkxx9//HqW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rV+/Xi+//LJGjRqlW2+91e2YuLg4HTx4UC1btlRQUJBLX1BQkBo2bKiFCxdq3759KlOmzPUoGwAAAACQi3l0sbHrISUlRV27dlXNmjU1cODADMfFxcVJkiIjI932O9ud4wAAAAAAuBYerUg7HT58WN988422bt2q5ORkTZkyRZJ07Ngx7d69W9WrV1dgYKBHcw8bNkxxcXFat26dfH19MxyXkJAgSQoNDXXbHxIS4jLuSikpKUpJSbG+TkxM9KheAAAAAMDNweMV6XfffVflypXTM888o7ffftvlcO+jR4/q9ttvd3thsKxYsWKFxo8frxdffFHVqlXztMQsGTNmjEJDQ60Hh38DAAAAADLjUZCeP3++nnnmGVWvXl3z5s3Tk08+6dJftWpVRUVFac6cObbnvnjxorp166aoqCgNHjz4quOdK9EZrTg7V5gzWrEeMmSIEhISrMe+ffts1wwAAAAAuHl4dGj3a6+9prJly2rJkiUKCgrSunXr0o2pXr26fv31V9tzJyUlWecz58mTx+2Y22+/XZL07bffWhchy+gc6KudQx0QEKCAgADbdQIAAAAAbk4eBemNGzeqS5cu6a6SfblSpUrpyJEjtucOCAjQY4895rZv+fLliouL03333aciRYooIiJCkZGRKlmypGJjY5WcnOxSU3JysmJjY1WuXDkO2QYAAAAAZAuPgnRaWpr8/f0zHXP06FGPVnoDAwM1efJkt33du3dXXFychgwZogYNGljtvXr10qhRozR69GiNHTvWah89erSSkpL0/PPP264DAAAAAAB3PArSlStXzvSw7YsXL2r58uWqXr26x4XZMXDgQM2dO1fjxo3Thg0bVKtWLa1fv14//fST6tatq759+16XOgAAAAAAuZ9HFxt75JFHtGHDBo0cOTJdX2pqqv73v/9p165d6tq16zUXmBVBQUFatmyZ+vbtqy1btmjChAnaunWr+vfvr8WLF3t8Cy4AAAAAAK7kMMYYuxtduHBBLVq00PLly1WhQgXlzZtXmzdvVocOHbR27VrFx8erRYsW+vHHH+VwOP6Nuv81iYmJCg0NVUJCgnUPam/UcURUTpcAAMilVp8bk9MlAAByofixrXO6hEzZyYIerUj7+/tr4cKFGjx4sE6cOKG//vpLxhh9/fXXOnnypAYNGqR58+bdcCEaAAAAAICr8egcaenSralefvllvfTSS9q2bZtOnjypkJAQ3XLLLfL19c3OGgEAAAAA8BoeB2knh8OhKlWqZEctAAAAAAB4vWsK0ikpKfrhhx+0YcMGJSQkKDQ0VLfddpvuvfdej259BQAAAACAt/M4SM+bN09PPPGEjh07psuvV+ZwOFS0aFF9+OGH+s9//pMtRQIAAAAA4C08CtKLFy9Whw4d5Ovrq549e6pRo0YqVqyYjhw5ouXLl+uzzz7T/fffr4ULF6pZs2bZXTMAAAAAADnGoyA9fPhwBQYG6vfff1e1atVc+rp27apnn31WDRs21PDhwwnSAAAAAIBcxaPbX23YsEEPPfRQuhDtFBUVpY4dO2r9+vXXVBwAAAAAAN7GoyCdL18+FSlSJNMxRYsWVb58+TwqCgAAAAAAb+VRkL7rrru0aNGiTMcsWrRId999t0dFAQAAAADgrTwK0uPHj9fRo0fVtWtX7du3z6Vv37596tKli44fP67x48dnS5EAAAAAAHgLjy421qVLFxUoUEAzZszQl19+qbJly1pX7d67d69SU1MVFRWlRx991GU7h8OhxYsXZ0vhAAAAAADkBI+C9NKlS63/v3jxonbt2qVdu3a5jNm0aVO67RwOhydPBwAAAACA1/AoSKelpWV3HQAAAAAA3BA8OkcaAAAAAICbFUEaAAAAAAAbCNIAAAAAANhAkAYAAAAAwAaCNAAAAAAANhCkAQAAAACwgSANAAAAAIAN2R6kU1JSdOHCheyeFgAAAAAAr+BRkF6+fLmGDRumU6dOWW0nTpzQPffco+DgYIWGhmrw4MHZVSMAAAAAAF7DoyA9fvx4ff755woLC7Pa+vfvr4ULF6pcuXIKCwvTa6+9plmzZmVXnQAAAAAAeAWPgvSGDRt05513Wl+fO3dOs2bNUosWLbR9+3Zt27ZNZcuW1XvvvZdthQIAAAAA4A08CtInTpxQqVKlrK9XrFihc+fOqUePHpKk/Pnzq02bNtq2bVv2VAkAAAAAgJfwKEgHBgbq9OnT1tdLliyRw+FQdHS01RYcHKx//vnn2isEAAAAAMCL+HmyUcWKFbVgwQKlpKTI4XDoyy+/1K233qrixYtbY/bu3auiRYtmW6EAAAAAAHgDj1akH3/8ce3YsUMVK1bULbfcop07d1qHdTutW7dOt956a7YUCQAAAACAt/AoSD/22GMaMGCAzp49q4SEBD355JPq27ev1b9ixQpt375dzZs3z646AQAAAADwCh4d2u1wODRu3DiNGzfObX/t2rX1zz//KCgo6JqKAwAAAADA23gUpK8mT548ypMnz78xNQAAAAAAOcqjQ7udvv32W3Xs2FFRUVGqWLGi1b5161a9+uqrOnDgwDUXCAAAAACAN/FoRTotLU2dOnXS119/LenS7bDOnj1r9RcoUEAvvPCCUlNTNWTIkOypFAAAAAAAL+DRivSkSZP01VdfqXfv3vrnn3/0v//9z6W/WLFiatSokb7//vtsKRIAAAAAAG/hUZCeNm2a6tatq3fffVchISFyOBzpxlSsWFG7d+++5gIBAAAAAPAmHgXpHTt2qFGjRpmOKVSokE6cOOFRUQAAAAAAeCuPgnRgYKASEhIyHbNnzx6FhYV5Mj0AAAAAAF7LoyB92223aeHChTp37pzb/pMnT2rBggVq0KDBNRUHAAAAAIC38ShIP/vss9q/f786dOig/fv3u/Tt3LlT7du3V0JCgp599tlsKRIAAAAAAG/h0e2v2rZtq0GDBmncuHEKDw9XUFCQJKlo0aI6ceKEjDEaOnSomjVrlq3FAgAAAACQ0zxakZakMWPGaOHChWrTpo3y5csnX19fpaWlqVWrVvrxxx81cuTI7KwTAAAAAACv4NGKtNPdd9+tu+++O7tqAQAAAADA63m8Ig0AAAAAwM3omlakU1NTtX//fh08eFAXLlxwO6Zx48bX8hQAAAAAAHgVj4J0WlqaXnnlFb3xxhs6efJkpmNTU1M9KgwAAAAAAG/kUZAeMmSIXnvtNRUtWlQ9evRQiRIl5Od3TYvbAAAAAADcEDxKv5988okqV66sNWvWKDg4OLtrAgAAAADAa3l0sbGkpCS1bt2aEA0AAAAAuOl4FKSjoqJ08ODB7K4FAAAAAACv51GQfuGFFzRnzhytX78+u+sBAAAAAMCreXSOdOvWrTVt2jTdc889uu+++1SjRg2FhIS4Hdu1a9drKhAAAAAAAG/iUZBOSUnR/Pnzdfz4cU2ZMkWS5HA4XMYYY+RwOAjSAAAAAIBcxaMgHRMToxkzZigqKkoPPPAAt78CAAAAANw0PEq/X331lWrXrq0VK1YQoAEAAAAANxWPLjZ27tw5NW3alBANAAAAALjpeBSka9eurR07dmR3LQAAAAAAeD2PgvQrr7yiBQsW6LvvvsvuegAAAAAA8GoeHZv9888/q0mTJmrbtq2aNWuW4e2vHA6Hhg4des1FAgAAAADgLRzGGGN3Ix+frC1kOxwOpaam2i4qJyUmJio0NFQJCQkZ3hvbG3QcEZXTJQAAcqnV58bkdAkAgFwofmzrnC4hU3ayoEcr0kuWLPGoMAAAAAAAbnQeBeno6OjsrgMAAAAAgBuCRxcbAwAAAADgZnXNN4Let2+fDh48qJSUFLf9jRs3vtanAAAAAADAa3gcpOfPn68BAwYoLi4u03E32sXGAAAAAADIjEeHdi9dulTt27dXUlKSnnnmGRlj1LhxYz3xxBO69dZbZYxR69atNWzYsOyuFwAAAACAHOVRkB47dqyCg4O1bt06vfHGG5Kkpk2b6r333tOff/6pl19+WYsXL1bbtm2ztVgAAAAAAHKaR0F6zZo1ateunYoVK2a1paWlWf8/ZMgQ3XbbbaxIAwAAAAByHY+C9JkzZ1SqVCnr64CAACUmJrqMadCggWJjY6+tOgAAAAAAvIxHQbp48eI6duyY9XWpUqW0efNmlzEnTpzw6EJj586dU0xMjBo3bqySJUsqb968Kl68uBo2bKipU6fqwoUL6bZJTExUTEyMwsPDFRAQoIiICA0YMEBJSUn2dw4AAAAAgEx4FKRr1Kihv/76y/q6adOmWrJkib744gslJydr4cKFmjVrlqKiomzPnZSUpPfee08Oh0OtW7dWTEyM2rdvrwMHDqhnz55q06aNy2HkycnJio6O1qRJk1SlShX169dPlStX1vjx49WsWTOdO3fOk10EAAAAAMAtj25/dd999+mZZ57Rnj17FB4erueff16zZ8/Wo48++n8T+/nppZdesj13wYIFlZCQoDx58ri0X7x4UXfffbd++ukn/fjjj2rdurUk6dVXX9XGjRs1aNAgjR071ho/ePBgjRs3TpMmTdKQIUM82U0AAAAAANLxaEW6Z8+eOnPmjMLDwyVJ5cqV05o1a9SnTx+1aNFCjz/+uFatWqXGjRvbL8jHJ12Ili4F8/bt20uSduzYIUkyxmjy5MkKDg7W0KFDXcYPHTpUwcHBmjx5su0aAAAAAADIiEcr0u5UqFBB77zzTnZNl05aWpoWLFggSapWrZokKS4uTgcPHlTLli0VFBTkMj4oKEgNGzbUwoULtW/fPpUpU+Zfqw0AAAAAcPPwKEj7+vrq4Ycf1owZM7K7Hsv58+f1yiuvyBijEydOaPHixdq6dat69Oih5s2bS7oUpCUpMjLS7RyRkZFauHCh4uLiCNIAAAAAgGzhUZAOCQn514Pp+fPnNXLkSOtrh8Oh//3vfxozZozVlpCQIEkKDQ3NsM7Lx7mTkpKilJQU6+srb+MFAAAAAMDlPDpHul69etq0aVN21+IiODhYxhilpqZq3759eueddzR58mQ1adIkW8PumDFjFBoaaj1YuQYAAAAAZMajID1ixAj98ssv+vTTT7O7nnR8fHxUunRpPfnkk/rwww8VGxurl19+WdL/rURntOLsDNwZrVhL0pAhQ5SQkGA99u3bl817AAAAAADITTw6tPvnn39WkyZN1KNHD7311luqW7euihUrJofD4TLO4XCku5r2tWjRooUkaenSpZL+79xo57nSV7raOdSSFBAQoICAgGyrEQAAAACQu3kUpEeMGGH9/7p167Ru3Tq347I7SB88eFCS5O/vL+lSQC5ZsqRiY2OVnJzscuXu5ORkxcbGqly5chyuDQAAAADINh4F6SVLlmR3HZa///5bERERypcvn0v7mTNnFBMTI0m69957JV0K6r169dKoUaM0evRojR071ho/evRoJSUl6fnnn//XagUAAAAA3HyyFKTnzZunKlWqqFKlSpKk6Ojof62gWbNmaeLEibrzzjsVERGhkJAQHThwQD/++KNOnDihRo0aqV+/ftb4gQMHau7cuRo3bpw2bNigWrVqaf369frpp59Ut25d9e3b91+rFQAAAABw88nSxcbat2+vL7/80vq6fPnyevPNN/+Vgtq0aaOHH35Ye/fu1RdffKEJEyboxx9/VFRUlD744AP98ssvCgwMtMYHBQVp2bJl6tu3r7Zs2aIJEyZo69at6t+/vxYvXuwyFgAAAACAa5WlFWl/f39duHDB+jo+Pl6nTp36VwqqU6eO6tSpY2ub0NBQTZo0SZMmTfpXagIAAAAAwClLK9Jly5bVb7/9ptTUVKvtyit0AwAAAABwM8jSinTnzp01atQoFSxYUIUKFZIkTZo0SVOnTs10O4fDoZ07d157lQAAAAAAeIksBekXX3xRefPm1ffff6+DBw/K4XDIGCNjTKbbXa0fAAAAAIAbTZaCtJ+fnwYPHqzBgwdLknx8fNSvXz8NGzbsXy0OAAAAAABvk6VzpK80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qmLFihozZozS0tIyrPno0aPq16+fKlasqICAABUuXFgdOnTQX3/9ddX9BQBvMmXKFPn5+WngwIFq2rSpFi9erD179rgdGxcXpx49eqhcuXIKCAhQwYIFVaNGDfXt21fOG83ceeed8vPz06FDh9zO0bVrVzkcDuvzbOnSpXI4HBoxYoTWrl2ru+++W/nz51doaKjat2+v+Ph4t/Ps2rVLTzzxhFVL0aJF1aRJkwz/rb+S81zkAwcOqHPnzipcuLDy58+v1q1ba9euXZKkLVu2qF27dipYsKDy58+vBx54QEeOHHE73/z589W0aVOFhoYqMDBQNWrU0MSJE3Xx4kW34ydPnqxq1aopb968KlOmjAYOHKhz585lWO/p06c1fPhwVa1aVYGBgQoLC1PLli3122+/ZWl/gZuOAYDLPProo0aSmTlzZpbGjxo1ykgyM2bMSNe3detWI8m0bt3aaps6daqRZNq3b2/y5MljOnToYPr372/q1q1rJJmGDRumm+frr782efLkMf7+/ub+++83Q4YMMY899pipVq2aadu27VVr3L17t5FkGjdubMLCwsxdd91l+vfvb6Kjo63n/PXXX01gYKC57777TP/+/U3t2rWNJNOjR4908/33v/81kkypUqXMs88+a5599llTqlQpI8k8++yz6cb37NnTSDLlypUzMTEx5qmnnjKFCxc2bdq0MZJMt27dXMbv2LHDlC5d2kgyLVq0MP379zddunQx+fLlM0FBQWblypVX3WcA8AabN282ksy9995rjDHmk08+MZLM8OHD0409cOCACQsLM/7+/qZdu3Zm0KBB5plnnjEtW7Y0/v7+5sKFC8YYYz799FMjybz88svp5vjnn39MYGCgqVq1qtW2ZMkSq4bAwEBz7733mv79+5tmzZoZSaZChQrm7NmzLvP8+uuvJiQkxDgcDtOqVSszePBg07t3b1OvXj1Ts2bNLO27JBMVFWUiIiJMgwYNTExMjPXvfvny5c2ff/5pwsLCTPPmzU3//v1NkyZNjCTTtGnTdHNNmDDBSDIFCxY0ffr0Mf379zeRkZFGkmnXrp1JS0tzGe/8bC5WrJh55plnTL9+/UzZsmWt54+OjnYZf+LECVO1alXrM7Fv376mZ8+eplChQsbPz898++23Wdpn4GZCkAbgYu7cuUaSyZ8/v+nfv79ZuHChOX78eIbjDxw4YPz8/EyTJk3S9f3vf/8zksycOXOsNmeQ9vPzM7/99pvVfvHiReuXiBUrVljthw8fNkFBQSYoKMisX78+3XPs27fvqvvkDNKSzOuvv261p6WlmXvvvddIMmFhYS51nj9/3kRFRRk/Pz9z+PBhq33ZsmVGkrnlllvMqVOnrPaTJ0+aSpUqGUlm+fLlVrvzF7gaNWqYpKQkq33//v2mcOHCboP0HXfcYXx9fc2CBQtc2rdt22by589vqlevftV9BgBvEBMTYySZL774whhjzOnTp01QUJApW7asSU1NdRn75ptvpvt32unEiRPW/589e9YULFjQlC9fPl2AfPvtt9PN4fx3WJL58ssvXcZ36dLFpT5jjDl37pwpVaqU8fHxMT/++GO6WrLyuWOMsZ6zX79+Lu1PPvmk9bmT0WfSunXrrPYdO3YYPz8/U7RoUbN3716XOu+8804jyXz66adWe1xcnPHz8zOlSpUyR44csdoTEhJM5cqV3Qbpzp07G0nmo48+cmk/cuSIKVOmjClSpEi6PzYANzuCNIB0JkyYYIKDg61fApx/sX/66afN9u3b041v3769cTgcJi4uzmo7f/68KVq0qClRooS1imDM/wXprl27ppvH2ffmm29abePGjTOSzLBhwzzeH2eQrlChQrpfupwrG+5WAJx/0f/ll1+sNufqsrsV+xkzZhhJpmfPnlZbjx49jCQze/bsdONHjx6dLkivX78+3RyXc/5S+ueff151vwEgJ50/f94UKVLEhISEuIQw55FPCxcudBnvDNIffPDBVefu16+fkWQWLVrk0n7bbbeZgIAAl+DtDNKNGzdON4+zLyYmxmqbOXNmhp9TdkgywcHBJjk52aV9+fLlV/1M+vjjj60252fRuHHj0j1HbGyskWSaNWtmtY0cOdJIMhMmTEg3fvr06emC9LFjx4yvr6/LHJdzfl/mz5+fpf0GbhZcbAxAOjExMXr88ce1YMEC/f7771q7dq1WrVqld955R1OmTNHMmTN13333WeN79+6tb7/9VpMnT9bYsWMlSfPmzdPRo0f1/PPPy88v/T81tWvXTtdWunRpSdKpU6esttWrV0uSWrRocc37FRUVle687xIlSkiSatasmW68s+/gwYNW24YNGyTJ7T04mzZtKknauHGj1bZp0yZJUqNGjdKNd9e2cuVKSdKRI0c0YsSIdP1bt261/lutWrV0/QDgLebOnatjx47pscceU968ea32rl276rPPPtOUKVNc/m3/z3/+oyFDhujpp5/W4sWL1apVK0VHR6t8+fLp5n7iiSc0adIkffTRR2revLkkad26ddqwYYM6d+6sggULptsmJz53IiMjlS9fPpc252dLZp9JWf3cuf3225U3b95r+txZs2aNUlNTlZKS4vZzJy4uTtKlz502bdqk6wduVgRpAG7lz59fDz74oB588EFJUkJCgp5//nm9++67euyxx3TgwAHlyZNH0qVfNsqVK6dPPvlEL730kvz8/DR58mQ5HA499thjbucPCQlJ1+YM3KmpqVZbQkKCJKlUqVLXvE+ZPWdmfRcuXLDaEhMT5ePjoyJFiqQbX6xYMTkcDiUmJlptCQkJ8vHxUeHChd2Ov9LJkyclSd9//72+//77DPclo4ugAYC3mDJliqRLwflyzZs3V6lSpTR37lydPHnSCr0RERFauXKlRowYoR9++EGzZs2SJFWpUkWjRo2yPo+cbdHR0ZozZ45OnDihQoUKafLkyZKkxx9/3G09N/LnjuT+M8PhcKhYsWI6cOCA1easv2jRounGZ/a5Exsbq9jY2Az3hc8dwBVX7QaQJaGhoXr77bcVHh6u48eP688//7T6HA6HnnjiCR0+fFjz58/Xvn379NNPP6l58+ZuVxLsCAsLkySXXxJyUkhIiNLS0nTs2LF0fUePHpUxxuWXo9DQUKWlpVlXBr+cuyuzOrd96623ZC6dfuP20a1bt2zcKwDIXs7PAUmKjo6Ww+GwHr6+vjpw4IBSUlL02WefuWxXrVo1ff311zp58qRWrFihYcOG6fDhw3rooYfShbw+ffooJSVFn376qc6cOaMvvvhCkZGRbldu7fDGzx3J/WeGMUZHjhxJ97kjXfpMulJmnzv9+/fP9HNn+PDh2bI/QG5BkAaQZQ6HQ0FBQW77evToIX9/f02ePFkff/yx0tLSMlwVsKNevXqSZP1CltNuu+02SZdup3IlZ9vlh4nXqFFDkvTrr7+mG++urX79+pL+7zZkAHAjmjZtmtLS0nTnnXfqscceS/dw/jHQuWp9JX9/fzVo0EAjR47Um2++KWOMvvvuO5cx999/v4oUKaLJkyfrq6++UkJCgnr16nXNtd9InzurVq3SuXPnrulzp27dui63CwOQNQRpAC4++OADrVmzxm3fnDlztGXLFoWFhaU7P7dYsWJq166dFixYoPfee0+FCxdWu3btrrmebt26KTg4WBMmTHA5B8zpeq8YOH/5GzlyZLpDuEeOHOkyRpK6dOkiSRo1apTLYXEHDhzQG2+8kW7+evXqqX79+vriiy80c+bMdP1paWlatmxZ9uwMAPwLjDGaOnWqHA6HPvnkE02ePDndY9q0abr99tv1xx9/aO3atZIuneN8+b+rTs5V1MvPs5akPHnyqHv37vr777/1/PPPy9/fX927d7/m+u+77z6VLl1an332mRYuXJiu/3p/7nTu3Fl+fn6aOHGiy7nT58+f16BBgyTJZb87d+4sX19fTZw40WVVOjExUS+99FK6+YsXL66OHTvq999/12uvvWbdr/tyq1at0pkzZ7Jxr4AbH+dIA3Dx448/qk+fPqpYsaIaNmyokiVLKjk5WRs2bNCvv/4qHx8fvfvuuwoICEi3bZ8+ffTVV1/pyJEj6t+/v3UO9bUoWrSoPv30Uz388MOqV6+e7rvvPlWuXFnHjx/XqlWrFBERoTlz5lzz82RV48aN9d///ldvvfWWqlWrpg4dOsgYo9mzZ2v//v169tln1bhxY2t806ZN1aNHD02dOlXVq1dX+/btlZKSopkzZ6pBgwbpVlgk6YsvvlDTpk318MMP6/XXX1etWrUUGBiovXv3asWKFTp27JjOnTt33fYZAOz45ZdftHv37gwvFObUo0cPrVixQlOmTFGdOnU0ffp0ffDBB2rcuLEqVKigkJAQ/f333/rhhx9UsGBB9ejRI90cvXv31vjx43Xw4EF16NDB7XnBdgUEBGjWrFlq1aqV7rnnHrVq1Uo1atRQYmKiNm7cqDNnzlgXALseKlSooHHjxql///6KiopSx44dFRQUpPnz52vbtm1q27atHn30UWt8xYoVNWzYMA0fPtwa7+fnp9mzZysqKkrbtm1L9xzvvvuutm3bpoEDB2r69Om6/fbbFRYWpn379mnt2rWKi4vToUOH0l04DbiZEaQBuBg3bpwaNmyon3/+WcuXL9ehQ4ckXbroSrdu3fTf//7X7ZVPpUuhsWzZstq7d2+2HF7n1L59e61atUpjxozRsmXLNG/ePBUuXFg1a9bMlsPH7XrzzTd122236b333tOHH34oSapatapGjRrl9he9jz76SJUqVdJHH32kt99+W6VLl1ZMTIw6duzoNkiXK1dOGzZs0MSJEzVnzhxNnTpVvr6+KlGihBo3bqwHHnjgX99HAPCU83Dtq60OP/TQQ3ruuef0xRdfaOLEierUqZPOnTun2NhYrV69WikpKSpdurSefPJJDRgwQGXLlk03R4UKFdSwYUP99ttv2fp5cPvtt2v9+vUaM2aMFi5cqEWLFqlAgQK69dZb1adPn2x7nqyKiYlRxYoVNXHiRH322Wc6f/68KlWqpAkTJujZZ59Nd/XvYcOGqWTJkpo0aZI++OADFS1aVA8//LBGjRrlNgwXLFhQv//+u95++23NnDlTM2bMUFpamooXL64aNWpo6NChbi+aCdzMHMbd8RsA4IFDhw6pbNmyuv3227V8+fKcLgcAkMudO3dOpUuXVnBwsHbt2iUfH85aBHB98K8NgGzz+uuv6+LFi3ryySdzuhQAwE1g6tSpOnHihHr37k2IBnBdsSIN4JokJCTovffe0549ezR58mRVqlRJf/zxh3x9fXO6NABALjV27FgdO3ZMH3zwgYKCgrR9+3brtk8AcD0QpAFck/j4eJUrV0558+ZVgwYN9P7776ty5co5XRYAIBdzOBzy9/dXjRo19NZbb6lBgwY5XRKAmwxBGgAAAAAAGziZBAAAAAAAGwjSAAAAAADYQJAGAAAAAMAGgjQAAAAAADYQpAEAAAAAsIEgDQBAJrp37y6Hw6H4+PhrmiciIkIRERHZUlN2io+Pl8PhUPfu3bN13mnTpsnhcGjatGnZOi8AAN6AIA0AyPWcYbFVq1YZjlm6dKkcDof69OlzHSvDtXA4HGrSpInbPoI8AODf5JfTBQAA4M3GjBmjwYMHq1SpUjldyr+iVKlS2rJli0JDQ3O6FAAAbhgEaQAAMlGiRAmVKFEip8v41/j7+6tKlSo5XQYAADcUDu0GACATGZ0jffHiRY0ZM0YVKlRQ3rx5VbFiRY0ZM0a7du3K9JzjpKQkPffccypZsqQCAgIUFRWlr7/+2u3Y8+fPa+LEiapVq5aCgoKUP39+NWrUSPPmzcuwzl27dmnChAm69dZbFRAQcNVznzM6R/rQoUN67rnnFBkZqcDAQIWFhemWW25Rnz59lJCQkOmcV5o7d67q1aunfPnyqUiRIurZs6eOHDniduzu3bvVq1cvlS1bVgEBASpRooS6d++uPXv2WGOch+FL0rJly+RwOKzHtGnT1L17d/Xo0UOS1KNHD5f+y50+fVrDhw9X1apVrX1s2bKlfvvtt3R1NWnSRA6HQ+fOndOLL76oChUqyN/fXyNGjLD1WgAAcgdWpAEA8EDPnj01ffp0lS9fXk8//bRSUlI0adIkrVixIsNtLly4oBYtWuiff/5Rhw4ddObMGX355Zfq2LGjFixYoBYtWlhjU1JS1KpVKy1dulQ1a9bUY489pgsXLuj7779X27Zt9dZbb+mZZ55J9xz//e9/tXLlSrVu3Vr/+c9/VLRoUdv7dubMGTVs2FDx8fFq0aKF2rdvr/Pnz2v37t2aPn26/ve//2X5UPDZs2dr4cKFeuCBB3TXXXdp5cqVmjp1qn799VetXr1aBQoUsMauWrVKLVu2VHJystq0aaPIyEjFx8drxowZ+vHHH7VixQqVL19eERERGj58uEaOHKnw8HCXPwLUrFlTYWFhOnXqlObOnau2bduqZs2a6eo6efKkGjdurM2bN6thw4bq06ePEhMTNXfuXDVt2lRfffWV2rVrl267Dh06aNOmTWrVqpXCwsJUrlw5uy8vACA3MAAA5HK7d+82kkyFChXM8OHD3T66detmJJnevXu7bOts3717t9W2aNEiI8nUrFnTJCcnW+0HDx40xYoVM5JMt27dXOYJDw83kkzbtm1NSkpKurlatmzpMv755583kszQoUNNWlqa1Z6YmGjq1Klj8uTJYw4cOJCuztKlS5s9e/bYfm0ur3fevHlGkunbt2+68adPnzbnzp276rxTp041kowks2DBApe+wYMHG0nmmWeesdrOnz9vIiIiTP78+c369etdxv/666/G19fXtGnTxqVdkomOjs70+adOneq2v3PnzkaS+eijj1zajxw5YsqUKWOKFClizp49a7VHR0db3/MTJ05cbfcBALkcK9IAgJvGzp07NXLkyGue57PPPpMkDRs2TPny5bPaS5Qooeeee07PP/98httOmjRJefLksb5u3ry5wsPDtWbNGqstLS1N7733nipUqKCRI0e6HJKcP39+DRs2TPfdd5+++eabdKvSAwYMUNmyZa95HyUpMDAwXVtwcLCtOe666y61bNnSpe2FF17Q+++/r08//VRvvPGGfHx89N133yk+Pl6jRo3Sbbfd5jL+zjvvVNu2bTVnzhwlJiYqJCTE/s5c5vjx45o5c6aaNWum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",
      "text/plain": [
       "<Figure size 1000x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "width = 0.4\n",
    "fontsize = 14\n",
    "\n",
    "plt.rc(\"font\", size=fontsize)\n",
    "fig, ax = plt.subplots(1, 1, figsize=(10, 8))\n",
    "\n",
    "rects1 = ax.bar([0], sync_fps, width, color=\"#557f2d\")\n",
    "rects2 = ax.bar([width], async_fps, width)\n",
    "ax.set_ylabel(\"frames per second\")\n",
    "ax.set_xticks([0, width])\n",
    "ax.set_xticklabels([\"Sync mode\", \"Async mode\"])\n",
    "ax.set_xlabel(\"Higher is better\")\n",
    "\n",
    "fig.suptitle(\"Sync mode VS Async mode\")\n",
    "fig.tight_layout()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## `AsyncInferQueue`\n",
    "[back to top ⬆️](#Table-of-contents:)\n",
    "\n",
    "Asynchronous mode pipelines can be supported with the [`AsyncInferQueue`](https://docs.openvino.ai/2024/openvino-workflow/running-inference/integrate-openvino-with-your-application/python-api-exclusives.html#asyncinferqueue) wrapper class. This class automatically spawns the pool of `InferRequest` objects (also called “jobs”) and provides synchronization mechanisms to control the flow of the pipeline. It is a simpler way to manage the infer request queue in Asynchronous mode."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setting Callback\n",
    "[back to top ⬆️](#Table-of-contents:)\n",
    "\n",
    "When `callback` is set, any job that ends inference calls upon the Python function. The `callback` function must have two arguments: one is the request that calls the `callback`, which provides the `InferRequest` API; the other is called “user data”, which provides the possibility of passing runtime values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "def callback(infer_request, info) -> None:\n",
    "    \"\"\"\n",
    "    Define the callback function for postprocessing\n",
    "\n",
    "    :param: infer_request: the infer_request object\n",
    "            info: a tuple includes original frame and starts time\n",
    "    :returns:\n",
    "            None\n",
    "    \"\"\"\n",
    "    global frame_number\n",
    "    global total_time\n",
    "    global inferqueue_fps\n",
    "    stop_time = time.time()\n",
    "    frame, start_time = info\n",
    "    total_time = stop_time - start_time\n",
    "    frame_number = frame_number + 1\n",
    "    inferqueue_fps = frame_number / total_time\n",
    "\n",
    "    res = infer_request.get_output_tensor(0).data[0]\n",
    "    frame = postprocess(res, frame, inferqueue_fps)\n",
    "    # Encode numpy array to jpg\n",
    "    _, encoded_img = cv2.imencode(\".jpg\", frame, params=[cv2.IMWRITE_JPEG_QUALITY, 90])\n",
    "    # Create IPython image\n",
    "    i = display.Image(data=encoded_img)\n",
    "    # Display the image in this notebook\n",
    "    display.clear_output(wait=True)\n",
    "    display.display(i)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "def inferqueue(source, flip, fps, skip_first_frames) -> None:\n",
    "    \"\"\"\n",
    "    Define the main function for video processing with async infer queue\n",
    "\n",
    "    :param: source: the video path or the ID of your webcam\n",
    "    :retuns:\n",
    "        None\n",
    "    \"\"\"\n",
    "    # Create infer requests queue\n",
    "    infer_queue = ov.AsyncInferQueue(compiled_model, 2)\n",
    "    infer_queue.set_callback(callback)\n",
    "    player = None\n",
    "    try:\n",
    "        # Create a video player\n",
    "        player = utils.VideoPlayer(source, flip=flip, fps=fps, skip_first_frames=skip_first_frames)\n",
    "        # Start capturing\n",
    "        start_time = time.time()\n",
    "        player.start()\n",
    "        while True:\n",
    "            # Capture frame\n",
    "            frame = player.next()\n",
    "            if frame is None:\n",
    "                print(\"Source ended\")\n",
    "                break\n",
    "            resized_frame = preprocess(frame)\n",
    "            # Start the inference request with async infer queue\n",
    "            infer_queue.start_async({input_layer_ir.any_name: resized_frame}, (frame, start_time))\n",
    "    except KeyboardInterrupt:\n",
    "        print(\"Interrupted\")\n",
    "    # Any different error\n",
    "    except RuntimeError as e:\n",
    "        print(e)\n",
    "    finally:\n",
    "        infer_queue.wait_all()\n",
    "        player.stop()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Test the performance with `AsyncInferQueue`\n",
    "[back to top ⬆️](#Table-of-contents:)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAFoAoADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD8rKKKKsAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooA/9k=",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "average throughput in async mode with async infer queue: 103.81 fps\n"
     ]
    }
   ],
   "source": [
    "frame_number = 0\n",
    "total_time = 0\n",
    "inferqueue(source=video_path, flip=False, fps=30, skip_first_frames=800)\n",
    "print(f\"average throughput in async mode with async infer queue: {inferqueue_fps:.2f} fps\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  },
  "openvino_notebooks": {
   "imageUrl": "",
   "tags": {
    "categories": [
     "API Overview"
    ],
    "libraries": [],
    "other": [],
    "tasks": [
     "Object Detection"
    ]
   }
  },
  "vscode": {
   "interpreter": {
    "hash": "08af04953a73b86b66cc089a637d3d397b0b73ad05ea59846f770cc21ccdacba"
   }
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {
     "4ecf106c6ed841b8b894f447e66c41ba": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "DescriptionStyleModel",
      "state": {
       "description_width": ""
      }
     },
     "54393aef1aee4b2a981c7b6ee75ed916": {
      "model_module": "@jupyter-widgets/base",
      "model_module_version": "2.0.0",
      "model_name": "LayoutModel",
      "state": {}
     },
     "c9c9f01fc0014058909e1d61e7bdd56d": {
      "model_module": "@jupyter-widgets/controls",
      "model_module_version": "2.0.0",
      "model_name": "DropdownModel",
      "state": {
       "_options_labels": [
        "CPU",
        "AUTO"
       ],
       "description": "Device:",
       "index": 0,
       "layout": "IPY_MODEL_54393aef1aee4b2a981c7b6ee75ed916",
       "style": "IPY_MODEL_4ecf106c6ed841b8b894f447e66c41ba"
      }
     }
    },
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
